/*
    Copyright (C) 2011  Jiabo <jiabo2011@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
	use: -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE to support large file
*/

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <inttypes.h>
#include <malloc.h>
#include <string.h>

#include "block_dev.h"

struct BlockDriverState {
	int fd; /* if -1, only COW mappings */
	int32_t total_sectors;
	int read_only;

	char filename[1024];
};

struct cow_header_v2 {
	uint32_t magic;
	uint32_t version;
	char backing_file[1024];
	int32_t mtime;
	uint64_t size;
	uint32_t sectorsize;
};

void bdrv_close(BlockDriverState *bs)
{
	if (bs->fd > 0)
		close(bs->fd);
	free(bs);
}

BlockDriverState *bdrv_open(const char *filename)
{
	BlockDriverState *bs;
	int fd;
	int32_t size;
	struct cow_header_v2 cow_header;

	bs = malloc(sizeof(BlockDriverState));
	if(!bs)
		return NULL;
	bs->read_only = 0;
	bs->fd = -1;
	strcpy(bs->filename, filename);

	/* open standard HD image */
	fd = open(filename, O_RDWR/* | O_LARGEFILE*/);
	if (fd < 0) {
		/* read only image on disk */
		fd = open(filename, O_RDONLY/* | O_LARGEFILE*/);
		if (fd < 0) {
			perror(filename);
			return NULL;
		}
	}
	bs->fd = fd;

	/* see if it is a cow image */
	if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
		fprintf(stderr, "%s: could not read header\n", filename);
		goto fail;
	}

	/* standard raw image */
	size = lseek(fd, 0, SEEK_END);
	bs->total_sectors = size / 512;
    
	return bs;
 fail:
	bdrv_close(bs);
	return NULL;
}

/* return -1 if error */
int bdrv_read(BlockDriverState *bs, int64_t sector_num, 
              uint8_t *buf, int nb_sectors)
{
	int ret, fd;
	int32_t offset;

	fd = bs->fd;
	offset = 0;

	if (fd < 0) {
		/* no file, just return empty sectors */
		memset(buf, 0, nb_sectors * 512);
	} else {
		offset += sector_num * 512;
		lseek(fd, offset, SEEK_SET);
		ret = read(fd, buf, nb_sectors * 512);
		if (ret != nb_sectors * 512) {
			return -1;
		}
	}
	return 0;
}

void bdrv_get_geometry(BlockDriverState *bs, int32_t *nb_sectors_ptr)
{
	*nb_sectors_ptr = bs->total_sectors;
}


